package com.handwriting.backend.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.handwriting.backend.entity.DataFile;
import com.handwriting.backend.entity.DataFrom;
import com.handwriting.backend.entity.Dataset;
import com.handwriting.backend.mapper.DataFileMapper;
import com.handwriting.backend.mapper.DataFromMapper;
import com.handwriting.backend.mapper.DatasetMapper;
import com.handwriting.backend.message.DataFileInfo;
import com.opencsv.CSVReader;
import com.opencsv.exceptions.CsvValidationException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;

import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

@Component
public class DatasetService {
    private final DatasetMapper datasetMapper;
    private final DataFileMapper dataFileMapper;
    private final DataFromMapper dataFromMapper;

    @Value("${dataset.path}")
    private String datasetPath;

    public DatasetService(DatasetMapper datasetMapper, DataFileMapper dataFileMapper, DataFromMapper dataFromMapper) {
        this.datasetMapper = datasetMapper;

        this.dataFileMapper = dataFileMapper;
        this.dataFromMapper = dataFromMapper;
    }

    public List<Dataset> getAllDatasets() {
        //从数据库表 'Dataset'中查出所有数据集，以及每一个数据集使用的文件数，返回其列表
        return datasetMapper.selectList(null);
    }

    public void deleteDataset(int id) {
        //删除指定id的数据集
        datasetMapper.deleteById(id);
    }

    public void createDataset(String name, List<Integer> imageIndexes) {
        //在'Dataset'表中建立一个新的条目，根据给定的图片id列表ImageIndexes从DataFile表中选择对应的图片，并在DataFrom创建这些图片和所建立数据集的关系。
        Dataset dataset = new Dataset(0, name, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        datasetMapper.insert(dataset);
        int newId = dataset.getId();
        for (Integer i : imageIndexes) {
            dataFromMapper.insert(new DataFrom(i, newId));
        }
    }

    public void uploadDatasets(MultipartFile file) throws IOException, CsvValidationException {
        //在'ZipDataset'表中建立一个新的条目，name采用file的文件名。将这个文件保存到datasetPath中，解压到新的文件夹，然后根据这些文件夹里的文件名在DataFile离创建新的记录。
        StringBuilder curTarName = new StringBuilder(Objects.requireNonNull(file.getOriginalFilename()).substring(0, file.getOriginalFilename().length() - 4));
        File tarDir = new File(datasetPath, curTarName + "/");
        while (tarDir.exists()) {
            curTarName.append("(1)");
            tarDir.renameTo(new File(datasetPath, curTarName + "/"));
        }
        tarDir.mkdirs();

        ZipInputStream zipS = new ZipInputStream(file.getInputStream());
        ZipEntry zipEntry;
        List<Integer> indexesList = new ArrayList<>(), labelList = new ArrayList<>();
        List<String> nameList = new ArrayList<>();
        CSVReader csvReader;
        while ((zipEntry = zipS.getNextEntry()) != null) {
            String zipFileName = zipEntry.getName();
            if (zipFileName.endsWith("csv")) {
                csvReader = new CSVReader(new InputStreamReader(zipS));
                String[] rowStr;
                int i = 0;
                while ((rowStr = csvReader.readNext()) != null) {
                    if (i++ == 0) continue;
                    nameList.add(rowStr[0]);
                    labelList.add(Integer.parseInt(rowStr[1]));
                    i++;
                }
            } else {
                File tarFile = new File(tarDir, zipFileName);
                BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(tarFile));
                byte[] byteS = new byte[1024];
                int zipSNum;
                while ((zipSNum = zipS.read(byteS, 0, byteS.length)) > 0) {
                    outputStream.write(byteS, 0, zipSNum);
                }
                outputStream.close();
            }
        }
        for (int i = 0; i < nameList.size(); ++i) {
            DataFile df = new DataFile(0, curTarName + "/" + nameList.get(i), labelList.get(i));
            System.out.println("inserting" + df);
            dataFileMapper.insert(df);
            indexesList.add(df.getId());
        }
        createDataset(curTarName.toString(), indexesList);
    }

    public List<DataFileInfo> getDataFiles() {
        //在datafiles表里查询所有信息并返回
        List<DataFile> allDataFile = dataFileMapper.selectList(null);
        List<DataFileInfo> ansList = new ArrayList<>();
        for (DataFile idf:allDataFile) {
            List<DataFrom> idfDatafromList = dataFromMapper.selectList(new QueryWrapper<DataFrom>().eq("data_id", idf.getId()));
            List<Integer> idfDatasetList = new ArrayList<>();
            for (DataFrom idFrm:idfDatafromList) {
                idfDatasetList.add(idFrm.getDatasetId());
            }
            ansList.add(new DataFileInfo(idf.getId(), idf.getFilename(), idf.getLabel(), idfDatasetList));
        }
        return ansList;
    }

    public void getFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取完整的路径
        String uri = request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE).toString();
        // 获取映射的路径
        String pattern = request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE).toString();
        AntPathMatcher matcher = new AntPathMatcher();
        // 截取带“/”的参数
        String filename = matcher.extractPathWithinPattern(pattern, uri);
        System.out.println(filename);
        File file = new File(datasetPath, filename);
        BufferedInputStream bi = new BufferedInputStream(new FileInputStream(file));
        BufferedOutputStream bo = new BufferedOutputStream(response.getOutputStream());
        int len;
        byte[] bytes = new byte[4096];
        while ((len = bi.read(bytes)) != -1) {
            bo.write(bytes, 0, len);
        }
        bo.flush();
        bo.close();
        bi.close();
    }
}
